package wechat

import (
	"baokaobang/conf"
	"baokaobang/utils"
	"crypto/tls"
	"encoding/xml"
	"fmt"
	"net/http"
	"sort"
	"strings"
)

type MiniPayment struct {
	XMLName        xml.Name `xml:"xml"`              // XML根节点名字
	Appid          string   `xml:"appid"`            // 微信分配的小程序ID
	MchId          string   `xml:"mch_id"`           // 微信支付分配的商户号
	NonceStr       string   `xml:"nonce_str"`        // 随机字符串，长度要求在32位以内
	Sign           string   `xml:"sign"`             // 通过签名算法计算得出的签名值
	Body           string   `xml:"body"`             // 商品简单描述
	OutTradeNo     string   `xml:"out_trade_no"`     // 商户系统内部订单号，要求32个字符内，只能是数字、大小写字母_-|*且在同一个商户号下唯一
	TotalFee       string   `xml:"total_fee"`        // 订单总金额，单位为分
	SpbillCreateIp string   `xml:"spbill_create_ip"` // 支持IPV4和IPV6两种格式的IP地址。调用微信支付API的机器IP
	NotifyUrl      string   `xml:"notify_url"`       // 异步接收微信支付结果通知的回调地址，通知url必须为外网可访问的url，不能携带参数
	TradeType      string   `xml:"trade_type"`       // 小程序取固定值：JSAPI
}

// 微信小程序统一下单同步返回信息
type MiniPaymentSyncRes struct {
	ReturnCode string `xml:"return_code"`
	ReturnMsg  string `xml:"return_msg"`
	Appid      string `xml:"appid"`
	MchId      string `xml:"mch_id"`
	DeviceInfo string `xml:"device_info"`
	NonceStr   string `xml:"nonce_str"`
	Sign       string `xml:"sign"`
	ResultCode string `xml:"result_code"`
	ErrCode    string `xml:"err_code"`
	ErrCodeDes string `xml:"err_code_des"`
	TradeType  string `xml:"trade_type"`
	PrepayId   string `xml:"prepay_id"`
	CodeUrl    string `xml:"code_url"`
}

// 微信小程序统一下单异步返回信息
type MiniPaymentAsyncRes struct {
	ReturnCode         string `xml:"return_code"`
	ReturnMsg          string `xml:"return_msg"`
	Appid              string `xml:"appid"`
	MchId              string `xml:"mch_id"`
	DeviceInfo         string `xml:"device_info"`
	NonceStr           string `xml:"nonce_str"`
	Sign               string `xml:"sign"`
	SignType           string `xml:"sign_type"`
	ResultCode         string `xml:"result_code"`
	ErrCode            string `xml:"err_code"`
	ErrCodeDes         string `xml:"err_code_des"`
	Openid             string `xml:"openid"`
	IsSubscribe        string `xml:"is_subscribe"`
	TradeType          string `xml:"trade_type"`
	BankType           string `xml:"bank_type"`
	TotalFee           string `xml:"total_fee"`
	SettlementTotalFee string `xml:"settlement_total_fee"`
	FeeType            string `xml:"fee_type"`
	CashFee            string `xml:"cash_fee"`
	CashFeeType        string `xml:"cash_fee_type"`
	CouponFee          string `xml:"coupon_fee"`
	CouponCount        string `xml:"coupon_count"`
	CouponType0        string `xml:"coupon_type_0"`
	CouponId0          string `xml:"coupon_id_0"`
	CouponFee0         string `xml:"coupon_fee_0"`
	TransactionId      string `xml:"transaction_id"`
	OutTradeNo         string `xml:"out_trade_no"`
	Attach             string `xml:"attach"`
	TimeEnd            string `xml:"time_end"`
}

func NewMiniPayment(totalFee, outTradeNo string) *MiniPayment {
	miniPayment := &MiniPayment{
		Appid:          conf.MiniConfig.AppId,
		MchId:          conf.WechatPayment.MchId,
		NonceStr:       utils.GetRandomString(32),
		Sign:           "",
		Body:           "招办直朕服务费",
		OutTradeNo:     outTradeNo,
		TotalFee:       totalFee,
		SpbillCreateIp: "127.0.0.1",
		NotifyUrl:      "https://www.baokaobang.com/notify/mini",
		TradeType:      "JSAPI",
	}
	miniPaymentMap := map[string]string{
		"appid":            miniPayment.Appid,
		"mch_id":           miniPayment.MchId,
		"nonce_str":        miniPayment.NonceStr,
		"body":             miniPayment.Body,
		"out_trade_no":     miniPayment.OutTradeNo,
		"total_fee":        miniPayment.TotalFee,
		"spbill_create_ip": miniPayment.SpbillCreateIp,
		"notify_url":       miniPayment.NotifyUrl,
		"trade_type":       miniPayment.TradeType,
	}
	var keySlice []string
	for k, _ := range miniPaymentMap {
		keySlice = append(keySlice, k)
	}
	sort.Strings(keySlice)
	paramStr := ""
	for _, v := range keySlice {
		paramStr += fmt.Sprintf("%s=%s&", v, miniPaymentMap[v])
	}
	paramStr += fmt.Sprintf("key=%s", conf.WechatPayment.ApiKey)
	sign := strings.ToUpper(utils.Md5Encode(paramStr))
	miniPayment.Sign = sign
	return miniPayment
}

// 微信小程序统一下单
func (m *MiniPayment) Order() error {
	contentType := "application/xml;charset=utf-8"
	url := "https://api.mch.weixin.qq.com/pay/unifiedorder"
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr}
	content, err := xml.Marshal(m)
	if err != nil {
		return utils.NewError(0, fmt.Sprintf("支付失败：%s", err.Error()))
	}
	_, err = client.Post(url, contentType, strings.NewReader(string(content)))
	if err != nil {
		return utils.NewError(0, fmt.Sprintf("支付失败：%s", err.Error()))
	}
	return nil
}

// 同步返回信息签名校验
func (m *MiniPaymentSyncRes) CheckSign() bool {
	resMap := map[string]string{
		"return_code":  m.ReturnCode,
		"return_msg":   m.ReturnMsg,
		"appid":        m.Appid,
		"mch_id":       m.MchId,
		"device_info":  m.DeviceInfo,
		"nonce_str":    m.NonceStr,
		"result_code":  m.ResultCode,
		"err_code":     m.ErrCode,
		"err_code_des": m.ErrCodeDes,
		"trade_type":   m.TradeType,
		"prepay_id":    m.PrepayId,
		"code_url":     m.CodeUrl,
	}
	var keySlice []string
	for k, _ := range resMap {
		keySlice = append(keySlice, k)
	}
	sort.Strings(keySlice)
	paramStr := ""
	for _, v := range keySlice {
		if len(resMap[v]) > 0 {
			paramStr += fmt.Sprintf("%s=%s&", v, resMap[v])
		}
	}
	paramStr += fmt.Sprintf("key=%s", conf.WechatPayment.ApiKey)
	sign := strings.ToUpper(utils.Md5Encode(paramStr))
	return strings.EqualFold(sign, m.Sign)
}

// 异步返回信息签名校验
func (m *MiniPaymentAsyncRes) CheckSign() bool {
	resMap := map[string]string{
		"return_code":          m.ReturnCode,
		"return_msg":           m.ReturnMsg,
		"appid":                m.Appid,
		"mch_id":               m.MchId,
		"device_info":          m.DeviceInfo,
		"nonce_str":            m.NonceStr,
		"sign_type":            m.SignType,
		"result_code":          m.ResultCode,
		"err_code":             m.ErrCode,
		"err_code_des":         m.ErrCodeDes,
		"openid":               m.Openid,
		"is_subscribe":         m.IsSubscribe,
		"trade_type":           m.TradeType,
		"bank_type":            m.BankType,
		"total_fee":            m.TotalFee,
		"settlement_total_fee": m.SettlementTotalFee,
		"fee_type":             m.FeeType,
		"cash_fee":             m.CashFee,
		"cash_fee_type":        m.CashFeeType,
		"coupon_fee":           m.CouponFee,
		"coupon_count":         m.CouponCount,
		"coupon_type_0":        m.CouponType0,
		"coupon_id_0":          m.CouponId0,
		"coupon_fee_0":         m.CouponFee0,
		"transaction_id":       m.TransactionId,
		"out_trade_no":         m.OutTradeNo,
		"attach":               m.Attach,
		"time_end":             m.TimeEnd,
	}
	var keySlice []string
	for k, _ := range resMap {
		keySlice = append(keySlice, k)
	}
	sort.Strings(keySlice)
	paramStr := ""
	for _, v := range keySlice {
		if len(resMap[v]) > 0 {
			paramStr += fmt.Sprintf("%s=%s&", v, resMap[v])
		}
	}
	paramStr += fmt.Sprintf("key=%s", conf.WechatPayment.ApiKey)
	sign := strings.ToUpper(utils.Md5Encode(paramStr))
	return strings.EqualFold(sign, m.Sign)
}
